package logging

import (
	"bytes"
	"fmt"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/net/gclient"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gcfg"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/yiqiang3344/gf-micro/cfg"
	"github.com/yiqiang3344/gf-micro/response"
	"io"
	"net/http"
	"time"
)

func HttpLogFormatJsonMiddleware(r *ghttp.Request) {
	glog.SetDefaultHandler(HandlerJson)

	r.Middleware.Next()
}

func HttpAccessLogMiddleware(r *ghttp.Request) {
	var (
		scheme = "http"
		proto  = r.Header.Get("X-Forwarded-Proto")
		body   = r.GetRequestMap()
		res    interface{}
	)

	r.Middleware.Next()

	if r.TLS != nil || gstr.Equal(proto, "https") {
		scheme = "https"
	}

	if j, err := gjson.DecodeToJson(body); err == nil {
		accessLogReqLimit := gcfg.Instance().MustGet(r.GetCtx(), cfg.ACCESS_LOG_REQ_LENGTH_LIMIT, 0).Int()
		if accessLogReqLimit > 0 && len(j.MustToJsonString()) > accessLogReqLimit {
			body = map[string]interface{}{
				"__ignore__": "data too long, ignore.",
			}
		}
	}

	res = r.Response.BufferString()
	if j, err := gjson.DecodeToJson(res); err == nil {
		rTmp := new(response.DefaultHandlerResponse)
		if err = j.Scan(rTmp); err == nil {
			accessLogResLimit := gcfg.Instance().MustGet(r.GetCtx(), cfg.ACCESS_LOG_RES_LENGTH_LIMIT, 0).Int()
			if accessLogResLimit > 0 && len(j.MustToJsonString()) > accessLogResLimit {
				rTmp.Data = "data too long, ignore."
			}
			res = rTmp
		}
	}

	HttpAccessLog{
		HttpCode: r.Response.Status,
		Scheme:   scheme,
		Method:   r.Method,
		Host:     r.Host,
		Path:     r.URL.Path,
		Cost:     float64(gtime.TimestampMilli()-r.EnterTime.TimestampMilli()) / 1000,
		Req:      body,
		Res:      res,
		Ip:       r.GetClientIp(),
		Header:   r.Header,
	}.Log(r.GetCtx())
}

func HttpErrorLogMiddleware(r *ghttp.Request) {
	r.Middleware.Next()

	err := r.GetError()
	if err == nil {
		return
	}

	ErrorLog{
		Method: r.Method,
		Req:    r.GetRequestMap(),
	}.Log(r.Context(), err)
}

func HttpClientLogMiddleware(c *gclient.Client, r *http.Request) (response *gclient.Response, err error) {
	var (
		ctx    = r.Context()
		start  = time.Now()
		scheme = "http"
		proto  = r.Header.Get("X-Forwarded-Proto")
		req    = map[string]interface{}{}
	)
	if r.TLS != nil || gstr.Equal(proto, "https") {
		scheme = "https"
	}

	query, _ := gstr.Parse(r.URL.Query().Encode())
	bodyBytes, _ := io.ReadAll(r.Body)
	r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
	//先通过字符格式解析
	req, _ = gstr.Parse(string(bodyBytes))
	//如果解析失败，则尝试json格式解析
	if len(req) == 0 && len(bodyBytes) > 0 {
		if j, err1 := gjson.DecodeToJson(bodyBytes); err1 == nil {
			req = j.Map()
		}
	}

	response, err = c.Next(r)
	duration := time.Since(start)
	res := ""
	errStr := ""
	respStatus := 200
	if err != nil {
		respStatus = 500
		errStr = err.Error()
	} else if response != nil {
		respStatus = response.StatusCode
		resBytes, _ := io.ReadAll(response.Body)
		response.Body = io.NopCloser(bytes.NewBuffer(resBytes))
		res = string(resBytes)
	}
	HttpClientLog{
		HttpCode: respStatus,
		Scheme:   scheme,
		Method:   r.Method,
		Host:     r.Host,
		Path:     r.URL.Path,
		Cost:     fmt.Sprintf("%.3fms", float64(duration)/1e6),
		Query:    query,
		Req:      req,
		Res:      res,
		Error:    errStr,
		Header:   r.Header,
	}.Log(ctx)
	return
}
